package com.server.common.code.utils;

import org.springframework.util.ObjectUtils;

import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

public class OptionalUtils<T>  {
    private static final OptionalUtils<?> EMPTY = new OptionalUtils<>();

    private final T value;

    private OptionalUtils() {
        this.value = null;
    }

    public static<T> OptionalUtils<T> empty() {
        @SuppressWarnings("unchecked")
        OptionalUtils<T> t = (OptionalUtils<T>) EMPTY;
        return t;
    }

    private OptionalUtils(T value) {
        this.value = value;
    }

    public static <T> OptionalUtils<T> of(T value) {
        return new OptionalUtils<>(value);
    }

    public static <T> OptionalUtils<T> ofEmpty(T value){
        return ObjectUtils.isEmpty(value)?empty():of(value);
    }

    public T get() {
        if (!isPresent()) {
            throw new NoSuchElementException("No value present");
        }
        return value;
    }

    /**
     * 是否存在
     */
    public boolean isPresent() {
        return !ObjectUtils.isEmpty(value);
    }

    /**
     * 存在则 回调
     */
    public void ifPresent(Consumer<? super T> consumer) {
        if (isPresent())
            consumer.accept(value);
    }

    /**
     * 不存在则 回调
     */
    public void ifNotPresent(Runnable runnable) {
        if (!isPresent())
            runnable.run();
    }

    public OptionalUtils<T> filter(Predicate<? super T> predicate) {
        Objects.requireNonNull(predicate);
        if (!isPresent())
            return this;
        else
            return predicate.test(value) ? this : empty();
    }

    public<U> OptionalUtils<U> map(Function<? super T, ? extends U> mapper) {
        Objects.requireNonNull(mapper);
        if (!isPresent())
            return empty();
        else {
            return OptionalUtils.ofEmpty(mapper.apply(value));
        }
    }

    public<U> OptionalUtils<U> flatMap(Function<? super T, OptionalUtils<U>> mapper) {
        Objects.requireNonNull(mapper);
        if (!isPresent())
            return empty();
        else {
            return Objects.requireNonNull(mapper.apply(value));
        }
    }

    public T orElse(T other) {
        return isPresent() ? value : other;
    }

    public T orElseGet(Supplier<? extends T> other) {
        return isPresent() ? value : other.get();
    }

    public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
        if (isPresent()) {
            return value;
        } else {
            throw exceptionSupplier.get();
        }
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }

        if (!(obj instanceof OptionalUtils)) {
            return false;
        }

        OptionalUtils<?> other = (OptionalUtils<?>) obj;
        return Objects.equals(value, other.value);
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(value);
    }

    @Override
    public String toString() {
        return value != null
                ? String.format("OptionalUtils[%s]", value)
                : "OptionalUtils.empty";
    }
}
